package top.lingkang.finalsql.utils;

import top.lingkang.finalsql.annotation.Column;
import top.lingkang.finalsql.annotation.Id;
import top.lingkang.finalsql.annotation.Nullable;
import top.lingkang.finalsql.annotation.Table;
import top.lingkang.finalsql.config.SqlConfig;
import top.lingkang.finalsql.error.FinalCheckException;
import top.lingkang.finalsql.error.FinalException;
import top.lingkang.finalsql.type.*;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Blob;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

/**
 * @author lingkang
 * Created by 2022/4/11
 */
public class ClassUtils {
    public static final HashMap<Class<?>, TabCache> tabCache = new HashMap<>();
    // private static final HashMap<String, Class> typeMap;

    public static SqlConfig sqlConfig;

    // 字符串返回处理
    public static final HashMap<Class<?>, TypeHandler> typeHandler = new HashMap<>();

    static {
  /*      typeMap = new HashMap<>();
        typeMap.put("String", String.class);
        typeMap.put("Integer", Integer.class);
        typeMap.put("int", int.class);
        typeMap.put("Long", Long.class);
        typeMap.put("long", long.class);
        typeMap.put("Date", Date.class);
        typeMap.put("Double", Double.class);
        typeMap.put("double", double.class);
        typeMap.put("float", float.class);
        typeMap.put("Float", Float.class);
        typeMap.put("BigDecimal", BigDecimal.class);*/

        typeHandler.put(String.class, new StringTypeHandler());
        typeHandler.put(BigDecimal.class, new BigDecimalTypeHandler());
        typeHandler.put(BigInteger.class, new BigIntegerTypeHandler());
        typeHandler.put(Boolean.class, new BooleanTypeHandler());
        typeHandler.put(byte[].class, new ByteArrayTypeHandler());
        typeHandler.put(Byte[].class, new ByteObjectArrayTypeHandler());
        typeHandler.put(Date.class, new DateTypeHandler());
        typeHandler.put(Double.class, new DoubleTypeHandler());
        typeHandler.put(Float.class, new FloatTypeHandler());
        typeHandler.put(Integer.class, new IntegerTypeHandler());
        typeHandler.put(Long.class, new LongTypeHandler());
        typeHandler.put(Object.class, new ObjectTypeHandler());
        typeHandler.put(Blob.class, new BlobTypeHandler());

        // 特殊转化
        typeHandler.put(long.class, new LongTypeHandler());
        typeHandler.put(int.class, new IntegerTypeHandler());
        typeHandler.put(double.class, new DoubleTypeHandler());
        typeHandler.put(float.class, new FloatTypeHandler());
    }

    public static <T> Field getIdColumn(Field[] df) {
        for (Field field : df) {
            Id annotation = field.getAnnotation(Id.class);
            if (annotation != null) {
                return field;
            }
        }
        return null;
    }

    public static <T> Class<? extends Object> getClass(T entity) {
        if (!(entity instanceof Class)) {
            return entity.getClass();
        }
        try {// 类，需要实例化
            return ((Class<?>) entity).newInstance().getClass();
        } catch (Exception e) {
            throw new FinalException(e);
        }
    }

    public static Field[] getBeanField(Class<?> clazz) {
        TabCache cache = tabCache.get(clazz);
        if (cache != null) {
            return cache.getFields();
        }
        addToCache(clazz);
        return tabCache.get(clazz).getFields();
    }


    /**
     * 添加类到缓存
     */
    public static void addToCache(Class<?> clazz) {
        if (tabCache.containsKey(clazz)) {
            return;
        }
        if (clazz.getAnnotation(Table.class) == null) {
            throw new FinalCheckException("表对象无 @Table 注解: " + clazz);
        }

        TabCache cache = new TabCache();
        Field idColumnField = null;
        String idColumnName = null;
        List<String> columnName = new ArrayList<>();
        List<Field> columnField = new ArrayList<>();
        List<TypeHandler> typeHandlers = new ArrayList<>();

        List<String> fieldName = new ArrayList<>();
        List<Field> fields = new ArrayList<>();

        for (int i = 0; i < clazz.getDeclaredFields().length; i++) {
            Field field = clazz.getDeclaredFields()[i];
            fieldName.add(field.getName());
            fields.add(field);
            typeHandlers.add(setTypeHandler(field.getType()));

            Id id = field.getAnnotation(Id.class);
            if (id != null) {
                if (idColumnField != null) {
                    throw new FinalCheckException("表对象存在多个 @Id 注解: " + clazz);
                } else {
                    idColumnField = field;
                }
                // 插入ID类型
                cache.setIdType(id.value());

                // 缓存列名称
                Column column = field.getAnnotation(Column.class);
                String name = null;
                if (column != null) {
                    name = CommonUtils.isEmpty(column.value()) ? NameUtils.unHump(field.getName()) : column.value();
                } else {
                    name = NameUtils.unHump(field.getName());
                }

                columnName.add(name);
                columnField.add(field);

                idColumnName = name;
                continue;
            }
            Column column = field.getAnnotation(Column.class);
            if (column != null) {
                // 缓存列名称
                columnName.add(CommonUtils.isEmpty(column.value()) ? NameUtils.unHump(field.getName()) : column.value());
                columnField.add(field);
            }
        }
        // 校验
        if (columnField.isEmpty()) {
            throw new FinalCheckException("表对象无 @Id 或者 @Column  注解: " + clazz);
        }

        // 表相关
        cache.setColumnField(columnField.toArray(new Field[0]));
        cache.setColumnName(columnName.toArray(new String[0]));
        if (idColumnField != null) {
            cache.setIdColumnField(idColumnField);
            cache.setIdColumnName(idColumnName);
        }

        // 不一定表相关
        if (!fieldName.isEmpty())
            cache.setFieldName(fieldName.toArray(new String[0]));
        if (!fields.isEmpty()) {
            cache.setFields(fields.toArray(new Field[0]));
        }

        cache.setTypeHandler(typeHandlers.toArray(new TypeHandler[0]));

        // 基本信息
        cache.setClazz(clazz);
        cache.setTableName(NameUtils.getTableName(clazz, sqlConfig.getSqlDialect()));

        tabCache.put(clazz, cache);
    }

    public static TypeHandler setTypeHandler(Class<?> clazz) {
        TypeHandler type = typeHandler.get(clazz);
        return type == null ? new ObjectTypeHandler() : type;
    }

    @Nullable
    public static Id getId(Class<?> clazz) {
        addToCache(clazz);
        TabCache tabCache = ClassUtils.tabCache.get(clazz);
        if (tabCache != null && tabCache.getIdColumnName() != null) {
            return tabCache.getIdColumnField().getAnnotation(Id.class);
        }
        return null;
    }

    /**
     * 不需要缓存
     */
    @Nullable
    public static Field getField(String name, Field[] df) {
        for (Field field : df) {
            if (field.getName().equals(name))
                return field;
        }
        return null;
    }

    public static <T> Object getValue(T t, Field field) {
        try {
            field.setAccessible(true);
            return field.get(t);
        } catch (IllegalAccessException e) {
            throw new FinalException(e);
        }
    }


    public static <T> T getMapper(Class<T> clazz, InvocationHandler handler) {
        return (T) Proxy.newProxyInstance(
                clazz.getClassLoader(),
                new Class[]{clazz},
                handler);
    }

    public static Field getFieldByFieldName(String name, TabCache tabCache) {
        for (int i = 0; i < tabCache.getFieldName().length; i++) {
            if (tabCache.getFieldName()[i].equals(name))
                return tabCache.getFields()[i];
        }
        return null;
    }
}
